﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; 
using System.Threading.Tasks;
using SX.Redis;
using SX.Utils.Castle.Components.DictionaryAdapter;

namespace SX.Utils
{
    public static class RedisHelper
    {

        #region StackExchangeRedis

        private static PooledRedisClientManager _pollManager;
        /// <summary>
        /// 缓冲池
        /// </summary>
        /// <param name="readWriteHosts"></param>
        /// <param name="readOnlyHosts"></param>
        /// <returns></returns>
        public static PooledRedisClientManager CreateManager(string[] readWriteHosts, string[] readOnlyHosts)
        {
            return new PooledRedisClientManager(readWriteHosts, readOnlyHosts,
                new RedisClientManagerConfig
                {
                    MaxWritePoolSize = readWriteHosts.Length * 10,
                    MaxReadPoolSize = readOnlyHosts.Length * 5,
                    AutoStart = true,
                });
        }
        //private const string _defRedisConStr = "password@localhost:6380?ssl=true&db=1";
        private static string _redisConStr; 
        private static readonly object Locker = new object();
        private static bool _initialized = false;

        /// <summary>
        /// Redis初始化完毕
        /// </summary>
        public static bool Initialized
        {
            get
            {
                return _initialized;
            }
        }

        /// <summary>
        /// RedisConnectionString
        /// </summary>
        private static string RedisConStr
        {
            get
            {
                if (string.IsNullOrEmpty(_redisConStr))
                    return ConfigHelper.GetConn("redis");
                else
                    return _redisConStr;
            }
        }

        /// <summary>
        /// 初始化Redis
        /// default:"localhost:6379,name=RedisHelper,connectTimeout=180,password="
        /// </summary>
        /// <param name="conStr">Redis连接串</param>
        public static void InitRedis(string conStr)
        {
            _redisConStr = conStr;
            UnInitRedis();
            lock (Locker)
            {
                if (_pollManager == null)
                {
                    _pollManager = CreateManager(new string[] {conStr}, new string[] {conStr});
                    _initialized = true;
                }
            }
        }

        /// <summary>
        /// 反初始化
        /// </summary>
        public static void UnInitRedis()
        {
            lock (Locker)
            {
                _pollManager = null;
            }
        }

        /// <summary>
        /// Redis ConnectionMultiplexer Instance
        /// </summary>
        public static RedisClient GetRedis()
        {
            if (_pollManager == null)
            {
                InitRedis(RedisConStr);
            }

            return _pollManager.GetClient() as RedisClient;
        }  
        #endregion

        #region Key（键）完事儿

        /// <summary>
        /// KeyDelete
        /// </summary>
        /// <param name="key"></param>
        /// <param name="db"></param> 
        /// <returns></returns>
        public static bool KeyDelete(string key)
        {
            using (var redis = GetRedis())
            {
                return redis.Remove(key);
            } 
        }

         

        /// <summary>
        /// KeyDump
        /// </summary>
        /// <param name="key"></param> 
        /// <returns></returns>
        public static byte[] GetBytes(string key)
        {
            using (var redis = GetRedis())
            {
                return redis.GetBytes(key);
            }
            
        }

        /// <summary>
        /// KeyExists
        /// </summary>
        /// <param name="key"></param> 
        /// <returns></returns>
        public static bool ContainsKey(string key)
        {
            using (var Redis = GetRedis())
            {
                return Redis.ContainsKey(key);
            }
          
        }

        /// <summary>
        /// KeyExists
        /// </summary>
        /// <param name="key"></param>
        /// <param name="expiry"></param> 
        /// <returns></returns>
        public static bool KeyExpire(string key, TimeSpan expiry)
        {
            using (var Redis = GetRedis())
            {
                return Redis.ExpireEntryIn(key, expiry);
            }
            
        }

        /// <summary>
        /// KeyExpire
        /// </summary>
        /// <param name="key"></param>
        /// <param name="expiry"></param>
        /// <param name="db"></param> 
        /// <returns></returns>
        public static bool KeyExpire(string key, DateTime  expiry)
        {
            using (var Redis = GetRedis())
            {
                return Redis.ExpireEntryAt(key, expiry);
            }

          
        }

        /// <summary>
        /// 模糊匹配查询Key
        /// </summary> 
        /// <param name="pattern"></param> 
        /// <returns></returns>
        public static List<string> Keys( string pattern)
        {
            using (var Redis = GetRedis())
            {

                return Redis.SearchKeys(pattern);
            }
        }

        //EXPIREAT          在哪里？
        //KeyMigrate
        //KeyMigrateAsync
        //KeyMove
        //KeyMoveAsync
        //OBJECT            在哪里？
        //KeyPersist
        //KeyPersistAsync
        //PEXPIRE           在哪里？
        //PEXPIREAT         在哪里？
        //PTTL              在哪里？
        //KeyRandom
        //KeyRandomAsync
        //KeyRename
        //KeyRenameAsync
        //RENAMENX          在哪里？
        //KeyRestore
        //KeyRestoreAsync
        //Sort
        //SortAsync
        //TTL               在哪里？
        //KeyType
        //KeyTypeAsync
        //SCAN              在哪里？
        #endregion

        #region Object

        /// <summary>
        /// Get
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>  
        /// <returns></returns>
        public static T Get<T>(string key)
        {
            using (var Redis = GetRedis())
            {
                return DeserializeObject<T>(Redis.GetValue(key));
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="keys"></param>
        /// <param name="db"></param> 
        /// <returns></returns>
        public static List<T> Get<T>(List<string> keys)
        {
            using (var Redis = GetRedis())
            {
                var redisVals = Redis.GetValues(keys);
                List<T> res = new List<T>();
                foreach (var value in redisVals)
                {
                    res.Add(DeserializeObject<T>(value));
                }
                return res;
            }
        }
        /// <summary>
        /// Set
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>  
        public static void Set<T>(string key, T value)
        {
            using (var Redis = GetRedis())
            {
                Redis.SetEntry(key, SerializeObject(value));
            }
        }
        /// <summary>
        /// Set
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param> 
        /// <param name="timeSpan"></param> 
        public static void Set<T>(string key, T value,  TimeSpan timeSpan)
        {
            using (var Redis = GetRedis())
            {
                Redis.SetEntry(key, SerializeObject(value), timeSpan);
            }
        }
        /// <summary>
        /// Increment
        /// </summary>
        /// <param name="key"></param> 
        /// <returns></returns>
        public static long Increment(string key)
        {
            using (var Redis = GetRedis())
            {
                return Redis.IncrementValue(key);
            }
        }
        /// <summary>
        /// Increment
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param> 
        /// <returns></returns>
        public static long Increment(string key, long value)
        {
            using (var Redis = GetRedis())
            {
                return Redis.IncrementValueBy(key, value);
            }
        }
        /// <summary>
        /// Decrement
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param> 
        /// <returns></returns>
        public static long Decrement(string key)
        {
            using (var Redis = GetRedis())
            {
                return Redis.DecrementValue(key);
            }
        }
        /// <summary>
        /// Decrement
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param> 
        /// <returns></returns>
        public static long Decrement(string key, int value)
        {
            using (var Redis = GetRedis())
            {
                return Redis.DecrementValueBy(key, value);
            }
        }

        #endregion

        

        #region Hash完事儿
        /// <summary>
        /// HDel
        /// </summary>
        /// <param name="key"></param>
        /// <param name="hashid"></param>
        /// <param name="db"></param> 
        /// <returns></returns>
        public static bool HDel(string key, string hashid)
        {
            using (var Redis = GetRedis())
            { 
                return Redis.RemoveEntryFromHash(key, hashid);
            }
           
        }

        /// <summary>
        /// HDel
        /// </summary>
        /// <param name="key"></param>
        /// <param name="hashids"></param> 
        /// <returns></returns>
        public static bool HDel(string key, string[] hashids)
        {
            using (var Redis = GetRedis())
            {
                return Redis.RemoveEntryFromHash(key, hashids);
            }
        }

        /// <summary>
        /// HExists
        /// </summary>
        /// <param name="key"></param>
        /// <param name="hashid"></param>
        /// <param name="db"></param>
        /// <returns></returns>
        public static bool HExists(string key, string hashid, int db = 0)
        {
            using (var Redis = GetRedis())
            {
                return Redis.HashContainsEntry(key, hashid);
            }
        }

        /// <summary>
        /// HGet
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="hashid"></param>
        /// <param name="key"></param>
        /// <param name="db"></param> 
        /// <returns></returns>
        public static T HGet<T>(string key, string hashid)
        {
            using (var Redis = GetRedis())
            {
                return DeserializeObject<T>(Redis.GetValueFromHash(key, hashid));
            }
        }
         

        /// <summary>
        /// HGet
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="hashids"></param>
        /// <param name="db"></param> 
        /// <returns></returns>
        public static IDictionary<string, T> HGet<T>(string key, string[] hashids)
        {
            using (var Redis = GetRedis())
            {
                var dataList = Redis.GetValuesFromHash(key, hashids);
                var resDic = new Dictionary<string, T>();
                for (int i = 0; i < hashids.Length; i++)
                {
                    if (!dataList[i].IsNullOrEmpty())
                    {
                        resDic.Add(hashids[i], DeserializeObject<T>(dataList[i]));
                    }
                    else
                    {
                        resDic.Add(hashids[i], default(T));
                    }
                }
                return resDic;
            }
        }

        /// <summary>
        /// HGetAll
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="db"></param> 
        /// <returns></returns>
        public static IDictionary<string, T> HGetAll<T>(string hashId)
        {
            using (var Redis = GetRedis())
            {
                var dic = Redis.GetAllEntriesFromHash(hashId);
                var resDic = new Dictionary<string, T>();
                foreach (var key in dic.Keys)
                {
                    resDic.Add(key, DeserializeObject<T>(dic[key]));
                }
                return resDic;
            }
        }

        /// <summary>
        /// HIncrBy
        /// </summary>
        /// <param name="key"></param>
        /// <param name="hashid"></param>
        /// <param name="value"></param> 
        /// <returns></returns>
        public static long HIncrBy(string key, string hashid, int value = 1)
        {
            using (var Redis = GetRedis())
            {
                return Redis.IncrementValueInHash(hashid, key, value);
            }
        }

        /// <summary>
        /// HIncrByDouble
        /// </summary>
        /// <param name="key"></param>
        /// <param name="hashid"></param>
        /// <param name="value"></param> 
        /// <returns></returns>
        public static double HIncrByDouble(string key, string hashid, double value = 1.0)
        {
            using (var Redis = GetRedis())
            {
                return Redis.IncrementValueInHash(hashid, key, value);
            }
        }

        /// <summary>
        /// HDecrBy
        /// </summary>
        /// <param name="key"></param>
        /// <param name="hashid"></param>
        /// <param name="value"></param> 
        /// <returns></returns>
        public static long HDecrBy(string hashid, string key, int value = 1)
        {
            using (var Redis = GetRedis())
            {
                return Redis.IncrementValueInHash(hashid, key, value * -1);
            }
        }

        /// <summary>
        /// HDecrByDouble
        /// </summary>
        /// <param name="key"></param>
        /// <param name="hashid"></param>
        /// <param name="value"></param> 
        /// <returns></returns>
        public static double HDecrByDouble(string hashid, string key,  double value = 1.0)
        {
            using (var Redis = GetRedis())
            {
                return Redis.IncrementValueInHash(hashid, key, value * -1);
            }
        }

        /// <summary>
        /// HKeys
        /// </summary>
        /// <param name="key"></param> 
        /// <returns></returns>
        public static List<string> HKeys(string key)
        {
            using (var Redis = GetRedis())
            {
                return Redis.GetHashKeys(key);
            }
        }

        /// <summary>
        /// HLen
        /// </summary>
        /// <param name="hashid"></param> 
        /// <returns></returns>
        public static long HLen(string hashid)
        {
            using (var Redis = GetRedis())
            {
                return Redis.GetHashCount(hashid);
            }
        }

        /// <summary>
        /// HSet
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="hashid"></param>
        /// <param name="value"></param> 
        /// <returns></returns>
        public static bool HSet<T>(string hashid, string key, T value)
        {
            using (var Redis = GetRedis())
            {
                return Redis.SetEntryInHash(hashid, key, SerializeObject(value));
            }
        }

        /// <summary>
        /// HSet
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="hashid"></param>
        /// <param name="dic"></param> 
        public static void HSet<T>(string hashid, IDictionary<string, T> dic)
        {
            using (var Redis = GetRedis())
            {
                Dictionary<string, string> dataDictionary = new Dictionary<string, string>();
                foreach (string key in dic.Keys)
                {
                    dataDictionary.Add(key, JsonHelper.ToJson(dic[key]));
                }
                Redis.SetRangeInHash(hashid, dataDictionary);
            }
        }
        /// <summary>
        /// HSet
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="hashid"></param> 
        /// <param name="item"></param>  
        public static void HSet<T>(string hashid, KeyValuePair<string, T> item)
        {

            HSet(hashid, item.Key, item.Value);
        }

        /// <summary>
        /// HValues
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="hashid"></param> 
        /// <returns></returns>
        public static T[] HValues<T>(string hashid)
        {
            using (var Redis = GetRedis())
            {
                var redisValues = Redis.GetHashValues(hashid);

                var res = new List<T>();
                foreach (string value in redisValues)
                {
                    res.Add(JsonHelper.ToObject<T>(value));
                }
                return res.ToArray();
            }
        }
  
        #endregion

        #region Set完事儿
        /// <summary>
        /// SAdd
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param> 
        public static void SAdd<T>(string key, T value)
        {
            using (var Redis = GetRedis())
            {
                Redis.AddItemToSet(key, SerializeObject(value));
            }
        }

        /// <summary>
        /// 集合的基数(元素的数量)，或者返回0如果键不存在
        /// </summary>
        /// <param name="key"></param> 
        public static void SCard(string key)
        {
            using (var Redis = GetRedis())
            {
                Redis.GetSetCount(key);
            }
        }

          
        /// <summary>
        /// SPop
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="db"></param> 
        /// <returns></returns>
        public static T SPop<T>(string key)
        {
            using (var Redis = GetRedis())
            {
                return DeserializeObject<T>(Redis.PopItemFromSet(key));
            }
        }
        /// <summary>
        /// SRemove
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param> 
        /// <returns></returns>
        public static bool SRemove<T>(string key, T value)
        {
            using (var Redis = GetRedis())
            {
                return Redis.RemoveItemFromList(key, SerializeObject(value)) > 0;
            }
        } 
        #endregion

        #region List

        /// <summary>
        /// LPush
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="db"></param> 
        public static void LPush<T>(string key, T value)
        {
            using (var Redis = GetRedis())
            {
                Redis.EnqueueItemOnList(key, SerializeObject(value));
            }
        }

        /// <summary>
        /// RPop
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param> 
        /// <returns></returns>
        public static T RPop<T>(string key)
        {
            using (var Redis = GetRedis())
            {
                return DeserializeObject<T>(Redis.DequeueItemFromList(key));
            }
        }

        /// <summary>
        /// RPush
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="db"></param> 
        public static void RPush<T>(string key, T value)
        {
            using (var Redis = GetRedis())
            {
                Redis.PushItemToList(key, SerializeObject(value));
            }
        }

        /// <summary>
        /// LPop
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param> 
        /// <returns></returns>
        public static T LPop<T>(string key)
        {
            using (var Redis = GetRedis())
            {
                return DeserializeObject<T>(Redis.RemoveStartFromList(key));
            }
        }

        #endregion

        #region Subscriber
         

        #endregion

        #region Json
        private static T DeserializeObject<T>(string obj)
        {
            if (string.IsNullOrEmpty(obj))
                return default(T);
            return JsonHelper.ToObject<T>(obj);
        }
        private static string SerializeObject(object obj)
        {
            if (obj == null)
                return null;
            return JsonHelper.ToJson(obj);
        }
        #endregion
    }
}
